package jp.ac.kobe_u.cs.cream;

import jp.ac.kobe_u.cs.cream.*;
import java.util.*;

public class Serialized extends Constraint {
    private Variable v[];
    private int l[];
    private int order[];

    public Serialized(Network net, Variable v[], int l[]) {
	super(net);
	this.v = (Variable[])v.clone();
	this.l = (int[])l.clone();
	order = null;
    }

    protected Constraint copy(Network net) {
	return new Serialized(net, Constraint.copy(v, net), l);
    }

    protected class SerializedCondition extends Condition {
	private int code[][];

	public SerializedCondition() {
	    index = Serialized.this.getIndex();
	    code = new int[v.length][3];
	    for (int i = 0; i < code.length; i++) {
		Domain d = v[i].getDomain();
		code[i][0] = i;
		code[i][1] = ((IntDomain)d).value();
		code[i][2] = l[i];
	    }
	    Comparator comp = new Comparator() {
		    public int compare(Object o1, Object o2) {
			int k1 = ((int[])o1)[1];
			int k2 = ((int[])o2)[1];
			return (k1 < k2) ? -1 : (k1 == k2) ? 0 : 1;
		    }
		};
	    Arrays.sort(code, comp);
	}

	public void setTo(Network network) {
	    Serialized s = (Serialized)network.getConstraint(index);
	    if (code == null) {
		s.order = null;
	    } else {
		s.order = new int[code.length];
		for (int i = 0; i < s.order.length; i++) {
		    s.order[i] = code[i][0];
		}
	    }
	}

	public List operations() {
	    List operations = new LinkedList();
	    for (int i = 0; i < code.length - 1; i++) {
		if (code[i][1] + code[i][2] == code[i+1][1]) {
		    // adjacent
		    Operation op = new Swap(index, i, i+1);
		    operations.add(op);
		}
	    }
	    return operations;
	}
    }

    private class Swap extends Operation {
	private int index;
	private int i;
	private int j;

	public Swap(int index, int i, int j) {
	    this.index = index;
	    this.i = i;
	    this.j = j;
	}
	
	public void applyTo(Network network) {
	    Serialized s = (Serialized)network.getConstraint(index);
	    int t = s.order[i]; s.order[i] = s.order[j]; s.order[j] = t;
	}

	public boolean isTaboo(Operation op) {
	    if (! (op instanceof Swap))
		return false;
	    Swap swap = (Swap)op;
	    return index == swap.index && i == swap.i && j == swap.j;
	}
    }

    protected void clearCondition() {
	order = null;
    }

    protected Condition extractCondition() {
	return new SerializedCondition();
    }

    protected boolean isModified() {
	return isModified(v);
    }

    private boolean satisfySequential(Trail trail) {
	if (order == null)
	    return true;
	for (int k = 0; k < order.length - 1; k++) {
	    int i = order[k];
	    int j = order[k+1];
	    IntDomain d0 = (IntDomain)v[i].getDomain();
	    IntDomain d1 = (IntDomain)v[j].getDomain();
	    int diffMin = d1.max() - l[i] + 1;
	    int diffMax = d0.min() + l[i] - 1;
	    d0 = d0.delete(diffMin, IntDomain.MAX_VALUE);
	    if (d0.isEmpty())
		return false;
	    d1 = d1.delete(IntDomain.MIN_VALUE, diffMax);
	    if (d1.isEmpty())
		return false;
	    v[i].updateDomain(d0, trail);
	    v[j].updateDomain(d1, trail);
	}
	return true;
    }

    private boolean satisfySerialized(Trail trail) {
	for (int i = 0; i < v.length; i++) {
	    for (int j = 0; j < v.length; j++) {
		if (i == j)
		    continue;
		IntDomain d0 = (IntDomain)v[i].getDomain();
		IntDomain d1 = (IntDomain)v[j].getDomain();
		int diffMin = d1.max() - l[i] + 1;
		int diffMax = d1.min() + l[j] - 1;
		if (diffMin <= diffMax) {
		    d0 = d0.delete(diffMin, diffMax);
		    if (d0.isEmpty())
			return false;
		    v[i].updateDomain(d0, trail);
		}
	    }
	}
	return true;
    }

    protected boolean satisfy(Trail trail) {
	if (! satisfySequential(trail))
	    return false;
	return satisfySerialized(trail);
    }

    public String toString() {
	return "Serialized(" + Constraint.toString(v) + "," 
	    + Constraint.toString(l) + ")";
    }
}

